package xunlei1;

import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @program: gitee4_socket
 * @description:
 * @author: zy
 * @create: 2024-07-20 16:16
 */
public class Xunlei {

    private static Logger log= Logger.getLogger(  Xunlei.class.getName() );

   // private static volatile long  fileSizeDownLoaded=0;  //不能保证long原子性
   private static  long fileSizeDownLoaded=0;
   /// private static AtomicLong fileSizeDownLoaded=new AtomicLong(   0L );   //保证原子性



    public static void main(String[] args) throws IOException, InterruptedException {
        List<Thread> list=new ArrayList<>();
        long start=System.currentTimeMillis();


        String url="http://www.hostbuf.com/downloads/finalshell_install.exe";
       // String url = "https://pm.myapp.com/invc/xfspeed/qqpcmgr/download/QQPCDownload1530.exe";
        //String url="http://dl.baofeng.com/baofeng5/bf5_new.exe";                // 106 617 136
        //1. 获取要下载的文件的大小
        long fileSize=  getDownLoadFileSize(  url );
        log.info(  "待下载的文件大小："+fileSize );     //   74 400 313    74,540,513   ->  74 540 513    -> 74 540 513
        //2. 新文件保存的位置
        String newFileName=genFileName(  url  );
        log.info("下载后新文件名为："+   newFileName );
        String userhome=System.getProperty(  "user.home" );
        String downloadPath=userhome+ File.separator+   newFileName ;
        log.info("下载后新文件的路径:"+ downloadPath );
        //3. 创建此新文件(空 )
        RandomAccessFile raf=new RandomAccessFile(    downloadPath ,"rw"  );
        raf.setLength(   fileSize );
        raf.close();
        log.info("空的新文件创建成功...");
        //4. 获取本机的核数 => 线程数
        int threadSize=Runtime.getRuntime().availableProcessors();
        log.info("线程数:"+ threadSize );
        //5. 计算每个线程要下载的字节数
        long sizePerThread= getSizePerThread(  fileSize,   threadSize  );
        log.info("每个线程要下载的数据量为:"+ sizePerThread );
        //6. 循环创建线程，每个线程下载自己的部分
        for(   int i=0;i<threadSize;i++){
            DownLoadedSizeNotify dlsn=new DownLoadedSizeNotify() {
                @Override
                public void notifySize(long size) {
                    //fileSizeDownLoaded+=size;  //不能保证原子性
                    // 14 989
                    synchronized(   Xunlei.class  ) {   //  Xunlei在jvm中只有一个 Class对象，即只有一把锁.  重量级锁方案
                        fileSizeDownLoaded += size;
                        System.out.println("已下载的文件总大小：" + fileSizeDownLoaded);
                    }
                    // 21307
                    //方案三: 利用原子类型
//                   fileSizeDownLoaded.addAndGet(  size  );
//                   System.out.println("已下载的文件总大小：" + fileSizeDownLoaded.get()  );

                }
            };
            DownLoadTask task=new DownLoadTask(  i, fileSize, threadSize,sizePerThread,url,downloadPath,  dlsn  );
            Thread t=new Thread(   task    );
            t.start();
            list.add(   t );

        }

        for(   Thread t:list) {
            t.join();   //
        }

        System.out.println("主线程阻塞在此, 等待子线程执行完毕...");
        long end=System.currentTimeMillis();
        System.out.println("程序执行的总时长:"+  (end-start));   // 19939    19773
    }

    /**
     * 计算每个线程要下载的字节数
     * @param fileSize
     * @param threadSize
     * @return
     */
    private static long getSizePerThread(long fileSize, int threadSize) {
        return   fileSize%threadSize==0?fileSize/threadSize:   fileSize/threadSize+1;
    }

    /**
     * 根据日期和时间生成新文件的文件名
     * @param url
     * @return
     */
    private static String genFileName(String url){
        Date d=new Date();
        DateFormat df=new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String prefix=df.format( d );
        //后缀名
        String suffix= url.substring( url.lastIndexOf(".") );
        return prefix+suffix;
    }

    /**
     * 正式下载前，获取文件大小      "HEAD"
     * @param url
     * @return
     */
    private static long getDownLoadFileSize(String url) throws IOException {
        long fileSize=0;
        URL u=new URL(    url );
        HttpURLConnection connection= (HttpURLConnection) u.openConnection();
        //设置请求头
        connection.setRequestMethod("HEAD");   //head不下载文件内容，保儿取 响应头域( 文件大小 )
        connection.setConnectTimeout(  3000 );
        connection.connect();

        fileSize=connection.getContentLength();
        log.info(  "文件大小："+fileSize );
        return fileSize;
    }
}

/**
 * 下载的数据量的回调接口
 */
interface DownLoadedSizeNotify{
    public void notifySize(long size);
}